package ac.il.technion.cs236635.project.core;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;

import ac.il.technion.cs236635.project.core.policy.AcceptancePolicy;
import ac.il.technion.cs236635.project.core.policy.PolicyConfigurator;

import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;

@Singleton
public class JobsQueue {

	private int amountOfHighJobs = 0;
	
	private int amountOfLowJobs = 0;
	
	private static final JobsQueue _instance = Guice.createInjector( new PolicyConfigurator( )).getInstance( JobsQueue.class);

	private final List< ServerJob> highPriority = new ArrayList< ServerJob>( );
	
	private final List< ServerJob> lowPriority = new ArrayList< ServerJob>( );
	
	private final Object highLock = new Object( );
	
	private final Object lowLock = new Object( );
	
	private final Logger log = Logger.getLogger( JobsQueue.class);
	
	@Inject
	@Named( "policy")
	private AcceptancePolicy policy;
	
	public static JobsQueue getInstance( ) {
		synchronized( _instance) {
			return _instance;
		}
	}
	
	protected JobsQueue( ) {
		log.info( "Jobs queue has been initialized");
	}
	
	public void pushHighPriorityJob( final ServerJob job) {
		synchronized( highLock) {
			if ( policy.accept(job, highPriority)) {
				log.info( "New job has been submitted to high queue. Client name: " + job.getClientName( ) + ", job id: " + job.getJobId( ));
				amountOfHighJobs ++;
			}
		}
	}
	
	public void pushLowPriorityJob( final ServerJob job) {
		synchronized( lowLock) {
			if ( policy.accept( job, lowPriority)) {
				log.info( "New job has been submitted to low queue. Client name: " + job.getClientName( ) + ", job id: " + job.getJobId( ));
				amountOfLowJobs ++;
			}
		}
	}
	
	public ServerJob popHighPriorityJob( ) {
		synchronized( highLock) {
			if ( highPriority.size( ) == 0)
				return null;
			ServerJob job = highPriority.remove(0);;
			log.info( "High priority job has poped. Client name: " + job.getClientName() + ", job id: " + job.getJobId( ));
			amountOfHighJobs --;
			return job;
		}
	}
	
	public ServerJob popLowPriorityJob( ) {
		synchronized (lowLock) {
			if ( lowPriority.size( ) == 0)
				return null;
			ServerJob job = lowPriority.remove(0);
			log.info( "Low priority job has poped. Client name: " + job.getClientName() + ", job id: " + job.getJobId( ));
			amountOfLowJobs --;
			return job;
		}
	}
	
	public void pushFirstHighJob( final ServerJob job) {
		synchronized( highLock) {
			highPriority.add( 0, job);
			log.info( "Put the job, with id: " + job.getJobId( ) + " at first place. High priority queue.");
			amountOfHighJobs ++;
		}
	}
	
	public void pushFirstLowJob( final ServerJob job) {
		synchronized( lowLock) {
			lowPriority.add( 0, job);
			log.info( "Put the job, with id: " + job.getJobId( ) + " at first place. Low priority queue");
			amountOfLowJobs ++;
		}
	}
	
	public int getAllJobsNum( ) {
		synchronized( lowLock) {
			synchronized( highLock) {
				return amountOfLowJobs + amountOfHighJobs;
			}
		}
	}
	
	public boolean removeLowJobById( int id) {
		synchronized( lowLock) {
			Iterator<ServerJob> iterator = lowPriority.iterator( );
			while ( iterator.hasNext( )) {
				ServerJob job = iterator.next( );
				if ( job.getJobId() == id) {
					iterator.remove( );
					return true;
				}
			}
		}
		return false;
	}
	
	public boolean removeHighJobById( int id) {
		synchronized( highLock) {
			Iterator<ServerJob> iterator = highPriority.iterator( );
			while( iterator.hasNext( )) {
				ServerJob job = iterator.next( );
				if ( job.getJobId() == id) {
					iterator.remove( );
					return true;
				}
			}
		}
		return false;
	}
}